Skip to content

Conversation

erikhfj
Copy link
Member

@erikhfj erikhfj commented Nov 4, 2026

Client half of #76. The server half (resolver, paging, tests) is #77.

Stacked on #75 for the generated types, and it needs #77 at runtime: the page queries myCrate, which main does not resolve yet. Merge order: #72 → #77, and #75 → this. CI on this branch is green on its own, because the tests use mocked responses.

What was done

  • MY_CRATE operation plus a /crate route, lazy-loaded like the album page. The header gets a "Your crate" link with aria-current="page", and Discover no longer claims to be the current page when you are on the crate.
  • The page reuses AlbumCard and CrateButton, so a saved album looks exactly like it did in the results.
  • myCrate cache policy: keyArgs: false (one crate per user, and the user id never enters the cache key), a first page replaces the list, a continuation appends and deduplicates.
  • CrateButton evicts myCrate after add or remove. Without that the crate page keeps its own cached list and shows an album the user just removed.
  • Empty state with a link back to Discover, loading and error states, role="status" on the summary line, and a Load more button while there are more pages. The crate uses the button rather than infinite scroll: a personal list is short, and it does not need to fire requests while the user reads.

What was tested

Three component tests (mocked Apollo): the list renders with its count and a Remove control, the empty state offers the way back, and an axe-core run over the populated page.

The axe test caught something real: heading-order. The page h1 was followed by the album cards' h3. Adding an h2 for the list fixed it, and the test now asserts axe examined something before asserting it found no violations.

Test Files  16 passed (16)
     Tests  111 passed (111)

pnpm format:check && pnpm lint && pnpm typecheck && pnpm build passes. The lazy route splits out to CratePage-*.js 2.08 kB, and the main bundle is unchanged in size class (373.88 kB raw / 115.26 kB gzip, inside the 512 000-byte budget).

Note on the stack

This is the third PR in a stack, which is what the workflow document allows, but the merge order matters more than usual here. If it is easier to review as one change, say so and I will combine #77 and this one.

docs/architecture.md decision 5 is updated in #77, not here, so the decision record and the resolver land together.

The client's operation types were hand-written next to each operation, so they
could drift from schema.graphql without anything failing. They already had: the
GenreFacet shape was wrong in the M2 branch.

`client/codegen.ts` runs the GraphQL Code Generator client preset against the
same root schema.graphql the server reads at startup. Each operation stays where
it is used, but is declared with `graphql()` and its result types are derived
from the generated output, so a field renamed in the contract fails typecheck.

Generated files are committed, so a checkout still builds without running the
generator; CI regenerates and fails on any difference. Two deliberate choices:
test files are excluded from the document glob (a fixture operation would land in
the production bundle), and generated files are ignored by ESLint and Prettier
because reformatting them would only make the stale-file check noisy.

`ALBUM_SORTS` stays a runtime array - it is what the URL parser validates
against, and the generated enum is a type union with nothing to iterate - so a
new test compares it with the schema enum instead.
The server half is #77; this is the page that makes the field useful, and it
reuses the search result card so a saved album looks like the one that was saved.

- `MY_CRATE` operation and a `/crate` route behind the same lazy-loading as the
  album page. The header link carries `aria-current` like Discover does.
- `myCrate` gets its own cache policy: one list, `after` is paging and not part
  of the cache key. A new first page replaces the list; a continuation appends.
- `CrateButton` evicts `myCrate` after add or remove, because the crate page
  caches its own list and would otherwise show an album the user just removed.
- Empty, loading, error and "load more" states, with `role="status"` on the
  summary line.

The list needed an h2: the cards start at h3, and axe reported a heading-order
violation with only the page h1 above them. That is the kind of thing the axe
test is for - it passed on the crate page only after the heading was added.
@erikhfj erikhfj requested a review from a team as a code owner November 4, 2026 19:43
Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant